package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.common.aliyun.ScanImage;
import com.heima.common.aliyun.ScanText;
import com.heima.common.ocr.OcrGteImageContent;
import com.heima.feign.article.ArticleClient;
import com.heima.file.config.MinIOConfigProperties;
import com.heima.model.article.dto.ArticleDto;
import com.heima.model.dtos.ResponseResult;
import com.heima.model.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 张一恒
 * @version 1.0
 * @description: 自媒体发布文章自动审核
 * @date 2023/1/13 23:30
 */
@Service
@Slf4j
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Autowired
    private ScanText scanText;
    @Autowired
    private ScanImage scanImage;

    @Value("${imageUrl.httpHead}")
    private String httpHead;

    @Autowired
    private ArticleClient articleClient;

    @Autowired
    private WmChannelMapper wmChannelMapper;

    @Autowired
    private WmUserMapper wmUserMapper;


    @Autowired
    private ApplicationContext ap;

    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;

//    @Autowired
//    private FileStorageService fileStorageService;

    @Value("${tesseract.setDatapath}")
    private String datapath;

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;


    @Autowired
    private OcrGteImageContent openImageContent;

    /**
     * @param wmNewsId
     * @return com.heima.model.dtos.ResponseResult
     * @description 自媒体发布文章自动审核
     * @author 31115
     * @date 2023/1/13 23:31
     */
    @Override
    @Async
    public ResponseResult autoScanWmNews(Integer wmNewsId) {
        //查询自媒体发布文章信息
        WmNews wmNews = wmNewsMapper.selectById(wmNewsId);

        //获取文本及图片内容信息
        Map<String, Object> taskAndImagesAndOcrMap = getTaskOrImages(wmNews);
        if (taskAndImagesAndOcrMap == null) {
            log.error("获取文本及图片内容信息出错：{}", taskAndImagesAndOcrMap);
            return ResponseResult.errorResult(AppHttpCodeEnum.AUTO_SCAN_TEXT);
        }

        //过滤文本和图片内容字符串敏感词
        Boolean filtration =
                filtration(taskAndImagesAndOcrMap.get("content").toString(), taskAndImagesAndOcrMap.get("ocrImageContent").toString(), wmNewsId);
        if (!filtration) {
            return ResponseResult.errorResult(AppHttpCodeEnum.AUTO_SCAN_TEXT);
        }

        //自动审核文本内容
        Boolean textScanResult = autoScanWmNewsText(wmNewsId, taskAndImagesAndOcrMap.get("content").toString());
        if (!textScanResult) {
            return ResponseResult.errorResult(AppHttpCodeEnum.AUTO_SCAN_TEXT);
        }

        //自动审核图片
        Boolean imagesScanResult = autoScanWmNewsImages(wmNewsId, (List<String>) taskAndImagesAndOcrMap.get("images"));
        if (!imagesScanResult) {
            return ResponseResult.errorResult(AppHttpCodeEnum.AUTO_SCAN_IMAGES);
        }

        //自动审核图片内容字符串
        Boolean ocrImageContentScanResult = autoScanWmNewsocrImageContent(wmNewsId, taskAndImagesAndOcrMap.get(
                "ocrImageContent").toString());
        if (!ocrImageContentScanResult) {
            return ResponseResult.errorResult(AppHttpCodeEnum.AUTO_SCAN_IMAGES);
        }


        //调用feign接口封装到app_article表 获取articleId
        Long articleId = ap.getBean(WmNewsAutoScanService.class).saveArticle(wmNews);

        //修改自媒体发布文章信息状态信息和添加article_id
        ap.getBean(WmNewsAutoScanService.class).updateWmNewsTest(articleId, wmNews);


        log.info("自媒体发布文章自动审核完成");
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    //自动审核图片内容字符串
    private Boolean autoScanWmNewsocrImageContent(Integer wmNewsId, String ocrImageContent) {
        //如果等于空直接不用审核了放行
        if (StringUtils.isEmpty(ocrImageContent)) {
            return true;
        }
        try {
            //审核内容
            Map map = scanText.greeTextScan(ocrImageContent);
            if ("block".equals(map.get("suggestion"))) {
                log.info("自动审核图片内容字符串违规:{}", ocrImageContent);
                //修改自媒体文章内容失败 更改状态
                ap.getBean(WmNewsAutoScanService.class).updateReason(2, "自动审核图片内容字符串出现违规,审核不通过", wmNewsId);
                return false;
            } else if ("review".equals(map.get("suggestion"))) {
                log.info("自动审核图片内容字符串不确定:{}", ocrImageContent);
                //修改自媒体文章内容不确定 更改状态
                ap.getBean(WmNewsAutoScanService.class).updateReason(3, "自动审核图片内容字符串出现不确定因素,审核不通过,转人工审核", wmNewsId);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("自动审核图片内容字符串出错：{}", e.getMessage());
            return false;
        }
        return true;
    }


    //过滤敏感词
    private Boolean filtration(String content, String ocrImageContent, Integer wmNewsId) {

        String filtrationContent = content + ocrImageContent;

        //先查询出所有敏感词
        List<WmSensitive> wmSensitives = wmSensitiveMapper.selectList(null);
        if (wmSensitives == null) {
            return false;
        }

        List<String> sensitives = wmSensitives.stream().map(wm -> {
            return wm.getSensitives();
        }).collect(Collectors.toList());

        SensitiveWordUtil.initMap(sensitives);

        Map<String, Integer> words = SensitiveWordUtil.matchWords(filtrationContent);
        if (words.size() <= 0) {
            //说明匹配不到敏感词
            return true;
        }
        log.debug("敏感词：{}", words);
        ap.getBean(WmNewsAutoScanService.class).updateReason(2, "过滤文本或图片内容敏感词审核违规,审核不通过，敏感词：" + words, wmNewsId);
        return false;
    }

    //修改自媒体文章审核后状态
    @Transactional
    @Override
    public void updateReason(Integer status, String errorMes, Integer wmNewsId) {
        WmNews wmNews = new WmNews();
        wmNews.setStatus(status);
        wmNews.setReason(errorMes);
        wmNewsMapper.update(wmNews, Wrappers.<WmNews>lambdaQuery().eq(WmNews::getId, wmNewsId));
    }

    //修改自媒体发布文章信息状态信息和添加article_id
    @Transactional
    @Override
    public void updateWmNewsTest(Long articleId, WmNews wmNews) {
        wmNews.setStatus(9);
        wmNews.setArticleId(articleId);
        wmNews.setReason("图片内容审核成功,发布成功");
        wmNewsMapper.updateById(wmNews);
    }


    //调用feign接口封装到app_article表 获取articleId
    @Transactional
    @Override
    public Long saveArticle(WmNews wmNews) {

        ArticleDto dto = new ArticleDto();
        BeanUtils.copyProperties(wmNews, dto);


        //文章的布局
        dto.setLayout(Short.parseShort(wmNews.getType().toString()));
        //频道
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if (wmChannel != null) {
            dto.setChannelName(wmChannel.getName());
        }

        //作者
        dto.setAuthorId(wmNews.getUserId().longValue());
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (wmUser != null) {
            dto.setAuthorName(wmUser.getName());
        }

        //设置文章id
        if (wmNews.getArticleId() != null) {
            dto.setId(wmNews.getArticleId());
        }
        dto.setCreatedTime(new Date());

        ResponseResult<Long> articleId = articleClient.saveArticle(dto);
        if (articleId == null) {
            log.error("获取articleId失败，{}", articleId);
            throw new RuntimeException("服务器内部错误,请联系管理员");
        }

        return articleId.getData();
    }


    //自动审核图片
    private Boolean autoScanWmNewsImages(Integer wmNewsId, List<String> images) {

        //如果等于空直接不用审核了放行
        if (StringUtils.isEmpty(images)) {
            return true;
        }
        try {
            //图片内容
            //处理图片路径
            List<String> newImagesUrl = new ArrayList<>();
            for (String image : images) {
                if (!image.contains("/")) {
                    String imageUrl = httpHead + "/" + image;
                    newImagesUrl.add(imageUrl);
                } else {
                    int index = image.indexOf("/");
                    index = image.indexOf("/", index + 2);
                    String uri = image.substring(index);
                    String imageUrl = httpHead + uri;
                    newImagesUrl.add(imageUrl);
                }
            }


            WmNews wmNews = null;
            Map map = scanImage.imageScan(newImagesUrl);
            if ("block".equals(map.get("suggestion"))) {
                log.info("自动审核图片违规:{}", newImagesUrl);
                //修改自媒体文章内容失败 更改状态
                ap.getBean(WmNewsAutoScanService.class).updateReason(2, "自动审核图片违规,审核不通过", wmNewsId);
                return false;
            } else if ("review".equals(map.get("suggestion"))) {
                log.info("自动审核内容不确定:{}", newImagesUrl);
                //修改自媒体文章内容不确定 更改状态
                ap.getBean(WmNewsAutoScanService.class).updateReason(3, "自动审核图片出现不确定因素,审核不通过,转人工审核", wmNewsId);
                return false;
            }


        } catch (Exception e) {
            e.printStackTrace();
            log.error("自动审核图片出错：{}", e.getMessage());
            return false;
        }
        return true;
    }


    //自动审核文本内容
    private Boolean autoScanWmNewsText(Integer wmNewsId, String content) {
        //如果等于空直接不用审核了放行
        if (StringUtils.isEmpty(content)) {
            return true;
        }
        try {
            //审核内容
            WmNews wmNews = null;
            Map map = scanText.greeTextScan(content);
            if ("block".equals(map.get("suggestion"))) {
                log.info("自动审核内容违规:{}", content);
                //修改自媒体文章内容失败 更改状态
                ap.getBean(WmNewsAutoScanService.class).updateReason(2, "自动审核内容出现违规,审核不通过", wmNewsId);
                return false;
            } else if ("review".equals(map.get("suggestion"))) {
                log.info("自动审核内容不确定:{}", content);
                //修改自媒体文章内容不确定 更改状态
                ap.getBean(WmNewsAutoScanService.class).updateReason(3, "自动审核内容出现不确定因素,审核不通过,转人工审核", wmNewsId);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("自动审核内容出错：{}", e.getMessage());
            return false;
        }
        return true;
    }

    //获取文本及tp内容信息
    private Map<String, Object> getTaskOrImages(WmNews wmNews) {

        Map<String, Object> map = new HashMap<>();
        //取出文章内容中的图片url
        List<Map> taskOrImageList = JSON.parseArray(wmNews.getContent(), Map.class);
        //封装imageUrl
        List<String> url = new ArrayList<>();
        //封装task
        StringBuffer sb = new StringBuffer();
        //封装图片内容字符串
        StringBuilder ocrSb = new StringBuilder();

        for (Map maps : taskOrImageList) {
            if ("text".equals(maps.get("type"))) {
                //取出文本
                String task = maps.get("value").toString();
                //每取出一段插入一段
                sb.append(task);
            }

            if ("image".equals(maps.get("type"))) {
                //取出url
                String imageUrl = maps.get("value").toString();

                //取出图片内容字符串
                String result = null;
                if (!StringUtils.isEmpty(imageUrl) && imageUrl.contains("http")) {
                    try {
                        result = getOcrStr(imageUrl);
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error("ocr提取图片文字失败：{}", e.getMessage());
                        return null;
                    }
                }
                ocrSb.append(result);
                url.add(imageUrl);
            }
        }

        //拿出标题
        sb.append(wmNews.getTitle());

        //封面图片
        String images = wmNews.getImages();
        if (!StringUtils.isEmpty(images)){
            //转换格式
            String[] imageArray = images.split(",");
            List<String> imageList = Arrays.asList(imageArray);
            url.addAll(imageList);

            for (String imageUrl : imageList) {
                String ocrStr = null;
                try {
                    ocrStr = getOcrStr(imageUrl);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("ocr提取图片文字失败：{}", e.getMessage());
                    return null;
                }
                ocrSb.append(ocrStr);
            }
        }

        map.put("ocrImageContent", ocrSb);
        map.put("content", sb);
        map.put("images", url);
        return map;

    }

    //ocr 提取图片文字
    private String getOcrStr(String imageUrl){
        //桶名
        String bucket = minIOConfigProperties.getBucket();
        //切割url处理 出filePath
        String filePath = imageUrl.substring(imageUrl.indexOf(bucket));
        filePath = filePath.substring(filePath.indexOf("/"));

        //获取minio中的文件
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucket).object(filePath).build();

        //获取文件扩展名
        String extension = imageUrl.substring(imageUrl.lastIndexOf("."));

        File file = null;
        try {
            InputStream inputStream = minioClient.getObject(objectArgs);
            //创建临时文件
            file = File.createTempFile("image",extension);
            FileOutputStream outputStream = new FileOutputStream(file);
            IOUtils.copy(inputStream,outputStream);

            //处理图片
            String result = openImageContent.ocrGteImageContent(datapath, file);
            if (StringUtils.isEmpty(result)){
                return null;
            }

            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }finally {
            if (file!=null){
                file.delete();
            }
        }
    }
}
